use elasticsearch::params::ExpandWildcards;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::api::{cache, cache_insert_map, index, search};
use crate::config;

#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct ResultIndices {
    total: i32,
    size: i32,
    current: i32,
    data: Vec<Value>,
}

impl ResultIndices {
    pub fn page(value: Value, page: Option<i32>, size: Option<i32>) -> Self {
        let size_number = if size.is_some() { size.unwrap() } else { 20 };
        let page_number = if page.is_some() { page.unwrap() } else { 1 };
        // 根据size_number和page_number计算index_aliases分页
        if value.is_array() {
            let data_list: &Vec<Value> = value.as_array().unwrap();
            // data_list 中 Value 的key 为 index，index以英文句号开头的值所有Value移动到最后
            let mut new_data_list: Vec<Value> = Vec::new();
            for val in data_list.iter() {
                if let Some(index_value) = val.as_object().and_then(|obj| obj.get("index")) {
                    if let Some(index) = index_value.as_str() {
                        if !index.starts_with(".") {
                            new_data_list.push(val.clone());
                        }
                    }
                }
            }
            for val in data_list.iter() {
                if let Some(index_value) = val.as_object().and_then(|obj| obj.get("index")) {
                    if let Some(index) = index_value.as_str() {
                        if index.starts_with(".") {
                            new_data_list.push(val.clone());
                        }
                    }
                }
            }
            let data = new_data_list.into_iter()
                .skip(((page_number - 1) * size_number) as usize)
                .take(size_number as usize)
                .collect::<Vec<_>>();
            Self {
                total: value.as_array().unwrap().len() as i32,
                size: size_number,
                current: page_number,
                data,
            }
        } else {
            Self {
                total: 0,
                size: size_number,
                current: page_number,
                data: Vec::new(),
            }
        }
    }
}

// get_node
#[tauri::command]
pub async fn get_node() -> String {
    config::get_node().await
}

// set_node
#[tauri::command]
pub async fn set_node(node: String) {
    config::set_node(node).await
}

// get_optional_node
#[tauri::command]
pub async fn get_optional_node() -> Vec<String> {
    config::get_optional_node().await
}

#[tauri::command]
pub async fn refresh() {
    cache().await.invalidate_all();
}

#[tauri::command]
pub async fn get_indices(index: Option<String>, sort: Option<String>, status: Option<String>, page: Option<i32>, size: Option<i32>, refresh: Option<bool>) -> Option<ResultIndices> {
    let es_status: ExpandWildcards = index::conversion_status(status);
    let es_sort = index::conversion_sort(sort);
    let key = match index.clone() {
        None => {
            match &es_sort {
                Some(value) => {
                    format!("get_indices_{:?}_{}", es_status, value)
                }
                None => {
                    format!("get_indices_{:?}", es_status)
                }
            }
        }
        Some(value) => {
            match &es_sort {
                None => {
                    format!("get_indices_{}_{:?}", value, es_status)
                }
                Some(sort_value) => {
                    format!("get_indices_{}_{:?}_{}", value, es_status, sort_value)
                }
            }
        }
    };
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        index::cat_indices(index, es_status, es_sort).await
    }).await;
    Some(ResultIndices::page(result, page, size))
}

#[tauri::command]
pub async fn get_cat_indices(index: String) -> Option<Value> {
    refresh().await;
    index::cat_indices(Some(index), ExpandWildcards::All, None).await
}

#[tauri::command]
pub async fn get_indices_aliases(index: String, refresh: Option<bool>) -> Option<Value> {
    let key = format!("get_indices_aliases_{}", index);
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        index::indices_aliases(index).await
    }).await;
    Some(result)
}

// indices_stats
#[tauri::command]
pub async fn get_indices_stats(index: String, refresh: Option<bool>) -> Option<Value> {
    let key = format!("get_indices_stats_{}", index);
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        index::indices_stats(index).await
    }).await;
    Some(result)
}

// indices_mapping
#[tauri::command]
pub async fn get_indices_mapping(index: String, refresh: Option<bool>) -> Option<Value> {
    let key = format!("get_indices_mapping_{}", index);
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        index::indices_mapping(index).await
    }).await;
    Some(result)
}

// indices_settings
#[tauri::command]
pub async fn get_indices_settings(index: String, refresh: Option<bool>) -> Option<Value> {
    let key = format!("get_indices_settings_{}", index);
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        index::indices_settings(index).await
    }).await;
    Some(result)
}

#[tauri::command]
pub async fn get_indices_stats_docs_store(index: String, refresh: Option<bool>) -> Option<Value> {
    let stats_docs_stores = cache_insert_map("get_indices_stats_docs_store".to_string(), refresh.is_some() && refresh.unwrap(), || async {
        index::indices_stats_docs_store().await
    }).await;
    match stats_docs_stores.get("indices") {
        None => {
            None
        }
        Some(indices) => {
            indices.get(index).cloned()
        }
    }
}

#[tauri::command]
pub async fn action_add_aliases(index: String, alias: String) -> Option<Value> {
    index::add_aliases(index, alias).await
}

#[tauri::command]
pub async fn action_delete_aliases(index: String, alias: String) -> Option<Value> {
    index::delete_aliases(index, alias).await
}

// close_indices
#[tauri::command]
pub async fn action_close_indices(indices: Vec<String>) -> Option<Value> {
    index::close_indices(indices).await
}

// open_indices
#[tauri::command]
pub async fn action_open_indices(indices: Vec<String>) -> Option<Value> {
    index::open_indices(indices).await
}

// remove_indices
#[tauri::command]
pub async fn action_remove_indices(indices: Vec<String>) -> Option<Value> {
    refresh().await;
    index::remove_indices(indices).await
}

// create_index
#[tauri::command]
pub async fn action_create_index(index: String, shards: Option<String>, replicas: Option<String>, mapping: Option<String>) -> Option<Value> {
    index::create_index(index, shards, replicas, mapping).await
}

// search
#[tauri::command]
pub async fn search(index: String, query: Option<String>, page: Option<String>, size: Option<String>, refresh: Option<bool>) -> Option<Value> {
    let page_number = if page.is_some() { page.unwrap().parse::<i64>().unwrap() } else { 1 };
    let size_number = if size.is_some() { size.unwrap().parse::<i64>().unwrap() } else { 20 };
    let key = format!("search_{}_{:?}_{:?}_{:?}", index, query, page_number, size_number);
    let result = cache_insert_map(key, refresh.is_some() && refresh.unwrap(), || async {
        let from = (page_number - 1) * size_number;
        search::search(index, query, from, size_number).await
    }).await;
    Some(result)
}